package me.luger.project.fof.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import me.luger.core.commom.util.*;
import me.luger.core.dao.nutz.service.BaseService;
import me.luger.core.mvc.entity.IntCodeResult;
import me.luger.project.fof.entity.FundInfo;
import me.luger.project.fof.entity.FundNav;
import me.luger.project.fof.entity.FofResult;
import me.luger.project.fof.entity.ext.ProfitAndRiskHistogram;
import org.apache.commons.collections.map.HashedMap;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.net.URLDecoder;
import java.util.*;

@Repository
public class FofResultService extends BaseService<FofResult> {
    public FofResultService(Dao dao) {
        super(dao);
    }

    @Autowired
    private FundNavService fundNavService;

    @Autowired
    private FofWeightDailyService fofWeightDailyService;

    @Autowired
    private FundInfoService fundInfoService;

    /**
     * 获取FOF对应基金列表，包括每个基金的份额
     * @param fofId
     * @param status
     * @return
     */
    public List<FofResult> getFofResultById(Integer fofId, Integer status){
        String stt = "";
        if(status != null) {
            stt = " and status=@status";
        }
        Sql sql = Sqls.create("select fund_ext_code, fund_portion, fund_quantity, asset_strategy_id from fof_result where fof_id = @fofId " + stt);
        sql.params().set("fofId",fofId);
        sql.params().set("status",status);
        List<FofResult> frList = this.list(sql);
        return frList;
    }

    public List<FofResult> getFofResultById(Integer fofId){
        return getFofResultById(fofId, 1);
    }

    public List<FofResult> getFofResultByIdAndDate(Integer fofId, Date modDate){
        String stt = "";
        Sql sql = Sqls.create("select fund_ext_code, fund_portion, fund_quantity, asset_strategy_id from fof_result where fof_id = @fofId and mod_date = @modDate" );
        sql.params().set("fofId",fofId);
        sql.params().set("modDate",modDate);
        List<FofResult> frList = this.list(sql);
        return frList;
    }

    public boolean saveFofResult(Integer fofId,List<FofResult> fofResultList){
        for(FofResult fofResult : fofResultList){
            fofResult.setFofId(fofId);
            fofResult.setModDate(DateUtil.nowAsDate());
            fofResult.setStatus(1);
            this.insert(fofResult);
        }
        return true;
    }



    public IntCodeResult saveFofResult(Integer fofId, String params,int paramHistoryId){
        JSONObject jsonObject = null;
        try{
            jsonObject = JsonUtil.parse2JsonObject(params);
            jsonObject = jsonObject.getJSONObject("step5");
        }catch (Exception e){
            LogUtil.errorLog(e,"传入的fof参数错误");
            return IntCodeResult.info(-1,"传入的fof参数错误");
        }

        JSONArray jsonArray = jsonObject.getJSONArray("fundList");

        if(null == jsonArray || jsonArray.size() == 0){
            return IntCodeResult.info(-2,"传入的fof参数中基金列表为空");
        }

        FofResult fofResult = null;
        Date modDate = DateUtil.nowAsDate();
        for(int i=0;i<jsonArray.size();i++){
            JSONObject itemObject = jsonArray.getJSONObject(i);
            String fundId = itemObject.getString("id");
            String weight = itemObject.getString("weight");
            if(StringUtil.isBlank(weight)){
                continue;
            }
            weight = weight.replaceAll("%","");
            String assetStrategyIdStr = itemObject.getString("assetStrategyId");
            if(!StringUtil.isNumeric(weight.replace(".",""))){//去掉小数点
                weight = "0";
            }
            double fundWeight = Double.valueOf(weight)/100;
            if(fundWeight <=0 ){
                continue;
            }
            FundInfo fundInfo = fundInfoService.getFundInfo(fundId);
            if(null == fundInfo){
                return IntCodeResult.info(-3,"未查到基金ID为【{1}】基金的信息",fundId);
            }

            int assetStrategyId = 0;
            if(StringUtil.isBlank(assetStrategyIdStr)){
                assetStrategyId = fundInfo.getDefaultAssetStrategyId();
            }else{
                assetStrategyId = Integer.parseInt(assetStrategyIdStr);
            }


            double fundQuantity = 0.0;
            if(null != fundInfo){
                fundQuantity = 1*fundWeight/fundInfo.getSwanav();
            }
            fofResult = new FofResult();

            fofResult.setFofId(fofId);
            fofResult.setFundExtCode(fundId);
            fofResult.setFundPortion(fundWeight);
            fofResult.setFundQuantity(fundQuantity);
            fofResult.setModDate(modDate);
            fofResult.setAssetStrategyId(assetStrategyId);
            fofResult.setFofParamId(paramHistoryId);
            fofResult.setStatus(1);
            try{
                this.insert(fofResult);
            }catch (Exception e){
                LogUtil.errorLog(e,"插入FOF信息错误");
                return IntCodeResult.info(-4,"插入FOF信息错误");
            }
        }
        return IntCodeResult.info(200);
    }

    public IntCodeResult updateFofResult(Integer fofId, String params,int paramHistoryId){
        Sql updateSql = Sqls.create("update fof_result set status = 0 where fof_id = @fofId");
        updateSql.setParam("fofId",fofId);
        return saveFofResult(fofId,params,paramHistoryId);
    }
    /**
     * 返回FOF最早的配置组合日期
     * @param fofId
     * @return
     */
    public Date getFofResultFirstDayById(Integer fofId){
        Date md = new Date();
        Sql sql = Sqls.create("select mod_date from fof_result where fof_id = @fofId order by mod_date asc limit 1");
        sql.params().set("fofId",fofId);
        List<FofResult> frList = this.list(sql);
        if(frList != null && frList.size() > 0) {
            FofResult fr = frList.get(0);
            md = fr.getModDate();
        }
        return md;
    }

    public List<String> getFofFundExtCodeList(Integer fofId) {
        List<String> fecList = new ArrayList<String>();
        Sql sql = Sqls.create("select distinct(fund_ext_code) from fof_result where fof_id = @fofId");
        sql.params().set("fofId",fofId);
        List<FofResult> frList = this.list(sql);
        if(frList != null && frList.size() > 0) {
            for(FofResult fr : frList) {
                fecList.add(fr.getFundExtCode());
            }
        }
        return fecList;

    }

    /**
     * 计算FOF回测的净值
     * @param fofId
     * @param yearBackLength
     * @return
     */
    public Map<String, Object> calcFofNavBackTest(Integer fofId, Integer yearBackLength) {

        if(fofId == null) {
            return null;
        }
        if(yearBackLength == null) {
            yearBackLength = 1;
        }
        //所有历史FOF的组成，按照时间顺序放在fofDateMap中（暂未实现）
        //计算规则为：FOF回测净值为最早一个FOF组合往前回测
        //结束时间为当前时间
        Date endDate = new Date();
        Date startDate = getFofResultFirstDayById(fofId);
        List<FofResult> firstFrList = getFofResultByIdAndDate(fofId, startDate);
        if(firstFrList == null || firstFrList.size() < 1) {
            return null;
        }
        //调整回测时间
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        c.add(Calendar.YEAR, yearBackLength * -1);
        startDate = c.getTime();

        Map<String, List<FundNav>> fundTrendMap = fofWeightDailyService.getFofFundNavMap(firstFrList, startDate, endDate);

        Map<Date, Map<String, Double>> dateNavMap = new TreeMap<Date, Map<String, Double>>();
        for(FofResult fr : firstFrList) {
            String fec = fr.getFundExtCode();
            for(String m : fundTrendMap.keySet()) {
                String fundExtCode = m;
                if(fundExtCode.equals(fec)) {
                    List<FundNav> om = fundTrendMap.get(fundExtCode);
                    if(om == null) {
                        continue;
                    }
                    //设置基金回测净值的初始值为1，其他天除以这个值进行调整（计算子基金时不调整）
                    Double adjustScale = new Double(1);
                    for(int i = 0; i < om.size(); i++) {
                        FundNav fn = om.get(i);
                        if(i == 0) {
                        //    adjustScale = fn.getSwanav();
                        }
                        Date fundDate = fn.getStatisticDate();
                        Double priceDouble = fn.getSwanav() / adjustScale;
                        Map<String, Double> fundDateNav = dateNavMap.get(fundDate);
                        if(fundDateNav == null) {
                            fundDateNav = new HashedMap();
                        }
                        fundDateNav.put(fundExtCode, priceDouble);
                        dateNavMap.put(fundDate, fundDateNav);
                    }
                }
            }
        }

        //计算FOF回测净值
        Map<String, Double> frPortion = new HashedMap();
        for(FofResult fr : firstFrList) {
            String fec = fr.getFundExtCode();
            Double quantity = fr.getFundQuantity();
            frPortion.put(fec, quantity);
        }

        Map<String, Double> fofNavResultMap = new TreeMap();

        //设置基金回测净值的初始值为1，其他天除以这个值进行调整
        Double adjustScale = new Double(1);
        Integer adjustIndex = 0;
        for(Map.Entry<Date, Map<String, Double>> k : dateNavMap.entrySet()) {
            Date keyDate = k.getKey();
            Map<String, Double> excMap = k.getValue();
            Double nav = new Double(0);
            for(Map.Entry<String, Double> k2 : excMap.entrySet()) {
                for(Map.Entry<String, Double> frk : frPortion.entrySet()) {
                    if(frk.getKey().equals(k2.getKey())) {
                        //净值计算方法为：fund的swanav * fund的Portion
                        nav += k2.getValue() * frk.getValue();
                    }
                }
            }
            if(adjustIndex.intValue() == 0) {
                adjustScale = nav;
            }
            adjustIndex++;
            fofNavResultMap.put(DateUtil.format(keyDate, DateUtil.DATE_YYYY_MM_DD), nav / adjustScale);
        }
        //为了和测试的净值结果一样格式，调整数据
        List<Map<String, Object>> fofNavList = new ArrayList<Map<String, Object>>();
        for(Map.Entry<String, Double> k : fofNavResultMap.entrySet()) {
            String day = k.getKey();
            Double nav = k.getValue();
            Map<String, Object> m = new HashedMap();
            m.put("statisticDate", day);
            m.put("swanav", nav);
            fofNavList.add(m);
        }

        Map<String, Object> resultMap = new HashedMap();
        resultMap.put("fofId", fofId);
        resultMap.put("backTestPriceList", fofNavList);

        return resultMap;
    }

    /**
     * 计算FOF回测的净值，默认计算一年
     * @param fofId
     * @return
     */
    public Map<String, Object> calcFofNavBackTest(Integer fofId) {
        return calcFofNavBackTest(fofId, 1);
    }

    /**
     * 执行汉文的脚本获取收益/风险构成（子基金）柱状图数据
     * @param fofId
     * @param backYearLength
     * @return
     */
    public List<ProfitAndRiskHistogram> getProfitAndRisk(int fofId,int backYearLength){
        String shell = "python /data/fof/comp/Return_and_Var.py %d %d";
        shell = String.format(shell,fofId,backYearLength);
        CommandResult result = ShellUtil.execCommand(shell);
        if(null == result || StringUtil.isNotBlank(result.errorMsg)){
            return null;
        }
        try{
            String ret = result.responseMsg;
            ret = ret.replaceAll("u'","'").replace("'","\"");
            System.err.println(ret);
            JSONObject jsonObject = JsonUtil.parse2JsonObject(ret);
            Set<String> allFundIds = jsonObject.keySet();
            List<ProfitAndRiskHistogram> profitAndRiskHistogramList = new ArrayList<ProfitAndRiskHistogram>();
            ProfitAndRiskHistogram profitAndRiskHistogram = null;
            for(String fundId : allFundIds){
                JSONArray profitAndRiskArr = jsonObject.getJSONArray(fundId);
                if(null == profitAndRiskArr || profitAndRiskArr.size() != 2){
                    throw new Exception(String.format("%s 计算错误",fundId));
                }
                FundInfo fundInfo = fundInfoService.getFundInfo(fundId);
                profitAndRiskHistogram = new ProfitAndRiskHistogram();
                profitAndRiskHistogram.setFundId(fundId);
                profitAndRiskHistogram.setName(fundInfo.getFundNameShort());
                profitAndRiskHistogram.setProfitPercent(profitAndRiskArr.getDouble(0));
                profitAndRiskHistogram.setRiskPercent(profitAndRiskArr.getDouble(1));

                profitAndRiskHistogramList.add(profitAndRiskHistogram);
            }
            return profitAndRiskHistogramList;
        }catch (Exception e){
            LogUtil.errorLog(e,"getProfitAndRisk error shell = " + shell);
            return null;
        }
    }
}
